# 重构版差异分析工具
# 专门处理HTML表格数据提取和Excel保存

import logging
import os
import re
from typing import List, Dict

from bs4 import BeautifulSoup


class HTMLTableProcessor:
    """HTML表格处理器 - 专门处理OCR返回的HTML表格数据"""

    def __init__(self, log_level=logging.INFO):
        """初始化处理器"""
        self.setup_logger(log_level)

    def setup_logger(self, log_level):
        """配置日志"""
        logging.basicConfig(
            level=log_level,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.StreamHandler()
            ]
        )
        self.logger = logging.getLogger(__name__)

    def process_html_tables(self, html_data_list: List[str]) -> List[Dict[str, str]]:
        """
        批量处理HTML表格数据
        
        参数:
            html_data_list: HTML字符串列表
            
        返回:
            List[Dict]: 所有表格的数据合并列表
        """
        all_data = []

        for i, html_content in enumerate(html_data_list):
            self.logger.info(f"处理第 {i + 1} 个HTML表格...")

            # 如果是字符串列表，取第一个元素
            if isinstance(html_content, list) and len(html_content) > 0:
                html_content = html_content[0]

            table_data = self.extract_table_from_html(html_content)
            if table_data:
                all_data.extend(table_data)
                self.logger.info(f"  提取到 {len(table_data)} 行数据")
            else:
                self.logger.warning(f"  未提取到有效数据")

        self.logger.info(f"总共提取到 {len(all_data)} 行数据")
        return all_data

    def extract_table_from_html(self, html_content: str) -> List[Dict[str, str]]:
        """从HTML内容中提取表格数据"""
        try:
            # 使用BeautifulSoup解析HTML
            soup = BeautifulSoup(html_content, 'html.parser')

            # 查找所有表格
            tables = soup.find_all('table')
            if not tables:
                self.logger.warning("HTML中未找到表格")
                return []

            # 处理第一个表格
            table = tables[0]
            return self._parse_table(table)

        except Exception as e:
            self.logger.error(f"解析HTML表格时出错: {str(e)}")
            return []

    def _parse_table(self, table) -> List[Dict[str, str]]:
        """解析单个表格"""
        all_rows = table.find_all('tr')

        if not all_rows:
            self.logger.warning("表格中未找到行数据")
            return []

        # 分析表格结构，找到表头和数据行
        headers, header_row_index = self._extract_headers(all_rows)
        data_rows = self._extract_data_rows(all_rows, headers, header_row_index)

        # 对提取的数据进行去空格处理
        cleaned_data_rows = self._remove_spaces_from_data(data_rows)

        self.logger.info(f"提取到表头: {headers}")
        self.logger.info(f"提取到 {len(cleaned_data_rows)} 行数据")

        return cleaned_data_rows

    def _extract_headers(self, all_rows) -> tuple:
        """提取表头信息"""
        headers = []
        header_row_index = -1

        # 遍历前几行寻找表头
        for row_idx, row in enumerate(all_rows[:5]):
            cells = row.find_all(['td', 'th'])
            potential_headers = []

            for cell in cells:
                cell_text = self._extract_cell_text(cell)
                if cell_text:
                    potential_headers.append(cell_text)

            # 如果这一行包含典型的表头关键词，认为是表头行
            header_keywords = ['顺序', '开户银行', '账号', '账户性质', '开户日期', '账户状态', '久悬日期', '销户日期']
            if any(keyword in ''.join(potential_headers) for keyword in header_keywords):
                headers = potential_headers
                header_row_index = row_idx
                self.logger.info(f"在第 {row_idx + 1} 行找到表头: {headers}")
                break

        # 如果没有找到明确的表头，使用默认表头
        if not headers:
            self.logger.warning("未找到明确的表头，使用默认表头")
            headers = ['顺序', '开户银行名称', '账号', '账户性质', '开户日期', '账户状态', '久悬日期', '销户日期']
            header_row_index = 0

        return headers, header_row_index

    def _extract_data_rows(self, all_rows, headers: List[str], header_row_index: int) -> List[Dict[str, str]]:
        """提取数据行"""
        data_rows = []

        # 从表头行之后开始处理数据行
        for row_idx, row in enumerate(all_rows):
            # 跳过表头行及之前的行
            if row_idx <= header_row_index:
                continue

            cells = row.find_all(['td', 'th'])
            if not cells:
                continue

            # 跳过明显的非数据行
            row_text = ''.join([self._extract_cell_text(cell) for cell in cells])
            skip_keywords = ['顺序', '开户银行', '存款人名称', '基本存款账户开户许可证', '填表说明']
            if any(keyword in row_text for keyword in skip_keywords):
                self.logger.debug(f"跳过非数据行 {row_idx}: {row_text[:50]}...")
                continue

            # 提取行数据
            row_data = {}
            for col_idx, cell in enumerate(cells):
                if col_idx < len(headers):
                    cell_text = self._extract_cell_text(cell)
                    row_data[headers[col_idx]] = cell_text

            # 验证是否为有效数据行
            if self._is_valid_data_row(row_data):
                data_rows.append(row_data)
                self.logger.debug(f"添加数据行 {row_idx}: {row_data}")
            else:
                self.logger.debug(f"跳过无效数据行 {row_idx}: {row_data}")

        return data_rows

    def _extract_cell_text(self, cell) -> str:
        """提取单元格文本内容"""
        text_parts = []

        # 查找所有span标签中的文本
        spans = cell.find_all('span')
        if spans:
            for span in spans:
                span_text = span.get_text(strip=True)
                if span_text and span_text not in text_parts:
                    text_parts.append(span_text)

        # 查找所有p标签中的文本（如果没有span标签）
        if not text_parts:
            paragraphs = cell.find_all('p')
            if paragraphs:
                for p in paragraphs:
                    # 先尝试从p标签内的span获取文本
                    p_spans = p.find_all('span')
                    if p_spans:
                        for span in p_spans:
                            span_text = span.get_text(strip=True)
                            if span_text and span_text not in text_parts:
                                text_parts.append(span_text)
                    else:
                        # 如果p标签内没有span，直接获取p标签文本
                        p_text = p.get_text(strip=True)
                        if p_text and p_text not in text_parts:
                            text_parts.append(p_text)

        # 如果还是没有文本，直接获取单元格文本
        if not text_parts:
            cell_text = cell.get_text(strip=True)
            if cell_text:
                text_parts.append(cell_text)

        # 合并文本内容，用空格连接
        combined_text = ' '.join(text_parts)

        # 清理文本
        cleaned_text = self._clean_text(combined_text)

        return cleaned_text

    def _clean_text(self, text: str) -> str:
        """清理文本内容"""
        if not text:
            return ""

        # 移除特殊字符
        text = text.replace('&nbsp;', '').replace('\xa0', '')

        # 去除前后空白
        text = text.strip()

        # 移除多余的空白字符
        text = re.sub(r'\s+', ' ', text)

        return text

    def _remove_spaces_from_data(self, data_rows: List[Dict[str, str]]) -> List[Dict[str, str]]:
        """
        对提取的数据进行去空格处理
        主要针对OCR识别可能产生的不连续文本问题
        """
        cleaned_rows = []

        for row in data_rows:
            cleaned_row = {}
            for key, value in row.items():
                if not value:
                    cleaned_row[key] = value
                    continue

                # 根据字段类型进行不同的去空格处理
                if key == '账号':
                    # 账号完全去除所有空格
                    cleaned_value = re.sub(r'\s+', '', value)
                elif key in ['开户银行名称']:
                    # 银行名称保留必要的空格，但去除多余空格
                    cleaned_value = re.sub(r'\s+', '', value)
                elif key in ['开户日期', '久悬日期', '销户日期']:
                    # 日期去除所有空格
                    cleaned_value = re.sub(r'\s+', '', value)
                elif key == '账户性质':
                    # 账户性质去除所有空格
                    cleaned_value = re.sub(r'\s+', '', value)
                elif key == '账户状态':
                    # 账户状态去除所有空格
                    cleaned_value = re.sub(r'\s+', '', value)
                else:
                    # 其他字段保持原有的空格处理逻辑
                    cleaned_value = re.sub(r'\s+', ' ', value).strip()

                cleaned_row[key] = cleaned_value

            cleaned_rows.append(cleaned_row)

        self.logger.info(f"完成数据去空格处理，处理了 {len(cleaned_rows)} 行数据")
        return cleaned_rows

    def _is_valid_data_row(self, row_data: Dict[str, str]) -> bool:
        """验证是否为有效的数据行"""
        # 检查是否有顺序字段且为数字
        sequence = row_data.get('顺序', '').strip()
        if sequence and sequence.isdigit():
            # 如果有顺序号，这很可能是数据行
            pass
        else:
            # 如果没有顺序号，检查其他关键字段
            account_number = row_data.get('账号', '').strip()
            if not account_number:
                return False

            # 检查账号是否包含数字（放宽验证条件）
            if not re.search(r'\d{10,}', account_number):
                return False

        # 检查是否有银行名称
        bank_name = row_data.get('开户银行名称', '').strip()
        if not bank_name or len(bank_name) < 3:
            return False

        # 检查是否包含明显的非数据内容
        all_text = ' '.join(row_data.values()).lower()
        invalid_keywords = ['填表说明', '存款人', '许可证', '核准号']
        if any(keyword in all_text for keyword in invalid_keywords):
            return False

        return True

    def save_to_csv(self, data: List[Dict[str, str]], output_path: str) -> bool:
        """将数据保存为CSV文件"""
        try:
            if not data:
                self.logger.warning("没有数据可保存")
                return False

            # 确保输出目录存在
            output_dir = os.path.dirname(output_path)
            if output_dir and not os.path.exists(output_dir):
                os.makedirs(output_dir)

            with open(output_path, 'w', encoding='utf-8-sig') as f:
                # 写入表头
                headers = list(data[0].keys())
                f.write(','.join(headers) + '\n')

                # 写入数据行
                for row in data:
                    values = [str(row.get(header, '')).replace(',', '，') for header in headers]
                    f.write(','.join(values) + '\n')

            self.logger.info(f"数据已成功保存到: {output_path}")
            self.logger.info(f"共保存 {len(data)} 行数据，{len(data[0])} 列")

            return True

        except Exception as e:
            self.logger.error(f"保存CSV文件时出错: {str(e)}")
            return False


def process_ocr_html_data(html_data_list: List[str], output_path: str = None) -> List[Dict[str, str]]:
    """
    处理OCR返回的HTML表格数据的主函数
    
    参数:
        html_data_list: HTML字符串列表
        output_path: 输出文件路径（可选）
        
    返回:
        List[Dict]: 提取的表格数据
    """
    # 创建处理器
    processor = HTMLTableProcessor()

    # 处理HTML表格数据
    table_data = processor.process_html_tables(html_data_list)

    # 保存结果
    if table_data and output_path:
        processor.save_to_csv(table_data, output_path)

    return table_data


def main():
    """主函数 - 示例用法"""
    # 示例：处理您提供的HTML数据
    sample_html_data = [
        '''<body>
<table border="1" border-collapse=collapse cellspacing="0">
<tr>
<td>顺序</td><td>开户银行名称</td><td>账号</td><td>账户性质</td><td>开户日期</td><td>账户状态</td><td>久悬日期</td><td>销户日期</td>
</tr>
<tr>
<td>1</td><td>中国建设银行股份有限公司武汉梨园支行</td><td>42001865757053005729</td><td>基本存款账户</td><td>2015年03月06日</td><td>正常</td><td></td><td></td>
</tr>
</table>
</body>'''
    ]

    # 处理数据
    result = process_ocr_html_data(sample_html_data, "重构版提取结果.csv")

    print(f"处理完成，提取到 {len(result)} 行数据")


if __name__ == "__main__":
    main()
